package alpha.com.google.code.mechanic.janitor.model;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * The class ContextsCollector is to manage user defined contexts. The Context
 * data is serializable. 
 *
 * Last_edited: Oct 14, 2014
 *
 * @History:
 * 1. add two new methods
 * @author jingjiejiang
 * @Edited: Oct 6, 2014
 * 1) Method List<String> getCtxNameList(): The method is to get a list of
 *    names of the user defined Contexts.
 * 2) Method String getFileLocFromName (String name): The method is to get
 *    the file location of the Context according to the Context name. 
 *    
 * 2. add a new method boolean isValidCtxName (String newname)  
 * @author jingjiejiang
 * @Edited: Oct 7, 2014
 * boolean isValidCtxName (String newname): The method is to check whether
 * the new Context duplicates one of the Context names that has already
 * been defined.
 * 
 * @since Apr 11, 2014
 *
 */
public class ContextsCollector {
  
  private final Set<IContext> contexts = new LinkedHashSet<IContext>();
  
  public void addContext (IContext context) {
    
    contexts.add(context);
  }
  
  public Set<IContext> getContextList() {
    
    return contexts;
  }
  
  public void copyAllContexts (Set<IContext> ctxset) {
    
    contexts.addAll(ctxset);
  }

  public void deleteContext (IContext context) {
    
    contexts.remove(context);
  }
  
  public IContext getContextFromName (String name) {
    
    Iterator<IContext> iterator = contexts.iterator();
    
    while (iterator.hasNext()) {
      
      IContext context = iterator.next();
      if (context.getName().equals(name)) {
        return context;
      }
    }
    
    return null;
  }
  
  public String getFileLocFromName (String name) {
    
    String location = "";
    
    IContext context = getContextFromName (name);
    location = context.getFileLocation();
    
    return location;
  }
  
  /**
   * Remove the selected context from the collector.
   */
  public void delSelContext() {
   
    Iterator<IContext> iterator = contexts.iterator();
    while (iterator.hasNext()) {
      
      IContext context = iterator.next();
      if (context.isSelected()) {
        contexts.remove(context);
        return;
      }
    }
    
  }
  
  /**
   * Get the selected context in the collector.
   * @return
   */
  public IContext getSelContext() {
    
    IContext context = null;
    
    Iterator<IContext> iterator = contexts.iterator();
    while (iterator.hasNext()) {
      
      IContext tempcontext = iterator.next();
      if (tempcontext.isSelected()) {
        
        context = tempcontext;
        break;
      }
    }
    
    return context;
  }
  
  /**
   * Set the selected context in the Contexts list as unselected one.
   *
   */
  public void clearContextSelection() {
    
    Iterator<IContext> iterator = contexts.iterator();
    while (iterator.hasNext()) {
      
      IContext context = iterator.next();
      if (context.isSelected()) {
        context.setSelected(false);
        return;
      }
    }
  }
  
  public void resetContextList() {
    
    contexts.clear();
  }
  
  /**
   * The method is to get a list of names of the user defined Contexts.
   * 
   * @return a list of Context names.
   *
   */
  public List<String> getCtxNameList() {
      
    List<String> list = new ArrayList<String>(); 
    
    for (IContext context: contexts) {
      list.add(context.getName());
    }
    
    return list;
  }
  
  /**
   * The method is to check whether the new Context duplicates one of the
   * Context names that has already been defined.
   * 
   * @param newname a new name of a new Context.
   * @return <code>true</code> the new Context name does not duplicate one
   *         of the existing Context names.
   *         <code>false</code> the new Context name duplicates one of the
   *         existing names.
   *
   */
  public boolean isValidCtxName (String newname) {
    
    List<String> list = getCtxNameList();
    boolean isvalid = true;
    
    for (String oldname: list) {
      if (oldname.equals(newname)) {
        return false;
      }
    }
    
    return isvalid;
  }
  
  @Override
  public String toString() {
    
    return contexts.toString();
  }
  
  
  /**
   * Serialize all the objects in the ContextCollector.
   *
   * @param file file that will be written to
   * @throws IOException 
   */
  public void serializeContextsInfo (File file) throws IOException {

    int objNum = 0;
    
    try{
      //use buffering
      OutputStream outputfile = new FileOutputStream(file);
      OutputStream buffer = new BufferedOutputStream(outputfile);
      ObjectOutput output = new ObjectOutputStream(buffer);
      
      try{
        
        objNum = contexts.size();
        
        output.writeInt(objNum*3);
        
        for (IContext context:contexts) {
          output.writeObject(context.getName());
          output.writeObject(context.getFileLocation());
          output.writeBoolean(context.isSelected());
        }
      }
      finally{
        output.close();
      }
    } catch(IOException e){
      throw new IOException ("An I/O error occurred when serialized the "
          + "Contexts: " + e.getMessage());
    }
  }
  
  /**
   * Read information regarding a group of IContext from a file.
   *
   * @param file The file that stores the Contexts data.
   * @param count The number of Contexts stores in the file.
   * @param context The new Context that is to receive the data.
   * @return
   * @throws Exception
   */
  public IContext deserializeContextInfo (File file, int count, IContext context) throws Exception {
    
    //int time = count;
    
    try {
      //use buffering
      InputStream inputfile = new FileInputStream(file);
      InputStream buffer = new BufferedInputStream(inputfile);
      ObjectInput input = new ObjectInputStream (buffer);
      
      try {
        
        input.readInt();
        
        for (; count >= 1; count --){
          
          context.setName((String)input.readObject());
          context.setFileLocation((String)input.readObject());
          context.setSelected(input.readBoolean());
          //count --;
        }
                   
      } catch (ClassNotFoundException e) {
        throw new Exception ("The class of a serialized object cannot be "
            + "found: " + e.getMessage());
      }
      finally {
        input.close();
      }
    }
    catch(IOException e) {
      throw new Exception("An I/O error occurred when deserialized Contexts "
          + "data: " + e.getMessage());
    }
    
    return context;
  }

  /**
   * Get the number of the contexts that is stored in the contexts.con file.
   * 
   * @param file file where read the data from
   * @return
   * @throws IOException 
   */
  public int getSavedCollectorSize(File file) throws IOException {
    
    int size = 0;
    
    try {
      //use buffering
      InputStream inputfile = new FileInputStream(file);
      InputStream buffer = new BufferedInputStream(inputfile);
      ObjectInput input = new ObjectInputStream (buffer);
      
      try {
        
        size = input.readInt();
      }
      finally {
        input.close();
      }
    }catch(IOException e) {
      throw new IOException ("An I/O error occurred: " + e.getMessage());
    }
    
    return size/3;
  }


}
